import { MODEL_NAME } from '../../constants/model-names';
import { EVENTS, type IEventBus } from '../../events';
import type { BaseSnapshot } from '../../systems/save';
import { BaseModel } from '../base';

// 战斗等级配置接口
interface BattleLevelConfig {
  baseExp: number; // 基础经验值
  maxLevel?: number; // 最大等级（可选）
}

// 等级信息接口
interface LevelInfo {
  level: number;
  currentExp: number; // 累计总经验值
  requiredExp: number; // 达到下一等级所需总经验值
  progress: number;
  isMaxLevel: boolean;
  currentLevelExp: number; // 当前等级内已获得经验值
  currentLevelRequiredExp: number; // 当前等级升级所需经验值
}

// 升级事件接口
export interface LevelUpEvent {
  oldLevel: number;
  newLevel: number;
  levelsGained: number;
  totalExpGained: number;
}

export interface BattleLevelV1 extends BaseSnapshot {
  version: 1;
  currentExp: number;
  level: number;
}

export type BattleLevelSnapshot = BattleLevelV1;
export type BattleLevelSnapshotOld = BattleLevelV1;

/**
 * 战斗等级管理器类
 * 负责处理战斗等级、经验值计算和升级逻辑
 */
export class BattleLevelModel extends BaseModel {
  public name = MODEL_NAME.BATTLE_LEVEL;
  private bus: IEventBus;
  private currentExp: number = 0;

  // 等级配置参数
  private readonly config: Required<BattleLevelConfig> = {
    baseExp: 100, // 基础经验值
    maxLevel: Infinity, // 最大等级限制
  };

  constructor(bus: IEventBus) {
    super();
    this.bus = bus;
  }

  /**
   * 计算从指定等级升级到下一级所需经验
   */
  private calculateExpForNextLevel(currentLevel: number): number {
    if (currentLevel < 1) return 0;
    // 边际成本 = scale * currentLevel
    return Math.floor(this.config.baseExp * currentLevel);
  }

  /**
   * 计算达到指定等级所需的总经验
   * @param targetLevel 目标等级
   * @returns 所需总经验值
   */
  private calculateTotalExpForLevel(targetLevel: number): number {
    if (targetLevel <= 1) return 0;
    const L = targetLevel;
    // 等差数列求和
    const total = (this.config.baseExp * (L * (L - 1))) / 2;
    return Math.min(Math.floor(total), Number.MAX_SAFE_INTEGER);
  }

  /**
   * 根据经验值计算当前等级
   * @param exp 当前经验值
   * @returns 计算出的等级
   */
  private calculateLevel(exp: number): number {
    if (exp <= 0) return 1;
    const scale = this.config.baseExp;
    // 求根公式
    const L = (1 + Math.sqrt(1 + (8 * exp) / scale)) / 2;
    let lvl = Math.floor(L);
    // 保护上限
    if (this.config.maxLevel !== Infinity) {
      lvl = Math.min(lvl, this.config.maxLevel);
    }
    return lvl;
  }

  /**
   * 设置当前经验值
   */
  public setCurrentExp(exp: number) {
    this.currentExp = exp;
  }

  /**
   * 获取当前等级信息
   * @returns 包含等级相关信息的对象
   */
  public getLevelInfo(): LevelInfo {
    const level = this.calculateLevel(this.currentExp);
    const isMaxLevel = level >= this.config.maxLevel;

    // 计算当前等级起始经验值
    const currentLevelStartExp = this.calculateTotalExpForLevel(level);
    // 计算下一等级所需总经验
    const totalExpForNextLevel = isMaxLevel
      ? currentLevelStartExp
      : currentLevelStartExp + this.calculateExpForNextLevel(level);

    const progress = isMaxLevel
      ? 1
      : (this.currentExp - currentLevelStartExp) /
        (totalExpForNextLevel - currentLevelStartExp);

    return {
      level,
      currentExp: this.currentExp,
      requiredExp: totalExpForNextLevel,
      progress: Math.min(1, Math.max(0, progress)),
      isMaxLevel,
      currentLevelExp: this.currentExp - currentLevelStartExp,
      currentLevelRequiredExp: totalExpForNextLevel - currentLevelStartExp,
    };
  }

  /**
   * 增加经验值
   * @param exp 要增加的经验值
   * @returns 升级事件信息，如果没有升级则返回null
   */
  public addExp(exp: number): LevelUpEvent | null {
    if (exp <= 0 || this.getLevelInfo().isMaxLevel) {
      return null;
    }

    const oldLevel = this.getLevelInfo().level;
    this.currentExp += exp;
    this.bus.emit(EVENTS.BATTLE_LEVEL_STATE_UPDATE);

    // 确保不超过最大等级对应的经验值
    if (this.config.maxLevel !== Infinity) {
      const maxExp = this.calculateTotalExpForLevel(this.config.maxLevel);
      this.setCurrentExp(Math.min(this.currentExp, maxExp));
    }

    const newLevel = this.getLevelInfo().level;
    const levelsGained = newLevel - oldLevel;

    // 如果等级提升，触发升级事件
    if (levelsGained > 0) {
      const event: LevelUpEvent = {
        oldLevel,
        newLevel,
        levelsGained,
        totalExpGained: exp,
      };
      this.bus.emit(EVENTS.BATTLE_LEVEL_UP, event);
      this.bus.emit(EVENTS.BATTLE_LEVEL_STATE_UPDATE);
      return event;
    }

    return null;
  }

  /**
   * 直接设置等级（用于调试或特殊功能）
   * @param targetLevel 目标等级
   * @param triggerEvent 是否触发升级事件
   * @returns 升级事件信息
   */
  public setLevel(
    targetLevel: number,
    triggerEvent: boolean = true
  ): LevelUpEvent | null {
    if (targetLevel < 1) targetLevel = 1;

    const oldLevel = this.getLevelInfo().level;

    // 确保不超过最大等级
    const actualTargetLevel = Math.min(targetLevel, this.config.maxLevel);
    const requiredExp = this.calculateTotalExpForLevel(actualTargetLevel);

    this.setCurrentExp(requiredExp);
    const newLevel = this.getLevelInfo().level;
    const levelsGained = newLevel - oldLevel;

    if (levelsGained > 0 && triggerEvent) {
      const event: LevelUpEvent = {
        oldLevel,
        newLevel,
        levelsGained,
        totalExpGained: requiredExp - this.currentExp,
      };

      this.bus.emit(EVENTS.BATTLE_LEVEL_UP, event);
      return event;
    }

    return null;
  }

  /**
   * 重置等级（保留配置）
   */
  public reset(): void {
    const oldLevel = this.getLevelInfo().level;
    this.setCurrentExp(0);

    if (oldLevel > 1) {
      this.bus.emit(EVENTS.BATTLE_LEVEL_RESET, { oldLevel, newLevel: 1 });
    }
  }

  /**
   * 获取配置信息
   */
  public getConfig(): Required<BattleLevelConfig> {
    return { ...this.config };
  }

  /**
   * 预测达到目标等级所需的总经验
   * @param targetLevel 目标等级
   * @returns 所需总经验值
   */
  public predictExpForLevel(targetLevel: number): number {
    if (targetLevel < 1) return 0;
    const actualTargetLevel = Math.min(targetLevel, this.config.maxLevel);
    return this.calculateTotalExpForLevel(actualTargetLevel);
  }

  /**
   * 检查是否达到最大等级
   */
  public isMaxLevel(): boolean {
    return this.getLevelInfo().isMaxLevel;
  }

  public get uiState() {
    return {
      levelInfo: this.getLevelInfo(),
    };
  }

  public save(): BattleLevelSnapshot {
    return {
      version: 1,
      currentExp: this.currentExp,
      level: this.getLevelInfo().level,
    };
  }

  public load(snapshot: BattleLevelSnapshot) {
    this.setLevel(snapshot.level);
    this.currentExp = snapshot.currentExp;
  }
}
